
package com.csliubo.eclipse.plugin.sqlgenerator.core;

import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;

/**
 * 管理数据库链接
 * 
 * @author liubo.cs@hotmail.com
 * @version 1.0
 */
public enum ConnectionManager {
    instance;

    private final String      protocol = "file:///";
    private Driver            driver   = null;
    private ConnectionWrapper conn     = null;

    /**
     * 获取数据库连接
     * 
     * @return
     */
    public static Connection getConnection() {
        return instance.getConnectionInternal();
    }

    /**
     * 打开数据库连接，这个连接会被共用
     */
    public static void open() {
        synchronized (instance) {
            try {
                instance.init();
            } catch (Throwable ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    /**
     * 关闭数据库连接
     */
    public static void close() {
        instance.closeInternal();
    }

    private Connection getConnectionInternal() {
        return instance.conn;
    }

    private void init() {
        try {
            Driver driver = getDriver();
            conn = new ConnectionWrapper(driver.connect(Config.getConnectionUrl(), getConnectionInfo()));
        } catch (Throwable ex) {
            throw new RuntimeException(ex);
        }
    }

    private void closeInternal() {
        conn.closeQuitely();
        conn = null;
    }

    private Properties getConnectionInfo() {
        Properties info = new Properties();
        info.put("user", Config.getDBUser());
        info.put("password", Config.getDBPassword());
        return info;
    }

    /**
     * 利用URLClassLoader从文件系统加载Driver
     * 
     * @return
     * @throws Throwable
     */
    private Driver getDriver() throws Throwable {
        if (null == driver) {
            synchronized (ConnectionManager.class) {
                if (null == driver) {
                    URL[] urls = new URL[1];
                    urls[0] = new URL(protocol + Config.getDriverLocation());
                    ClassLoader urlClassLoader = new URLClassLoader(urls);
                    Class<?> driverClass = urlClassLoader.loadClass(Config.getDriverClass());
                    driver = (Driver) driverClass.newInstance();
                }
            }
        }
        return driver;
    }

    class ConnectionWrapper implements Connection {

        private Connection delegate;

        public ConnectionWrapper(Connection conn) {
            this.delegate = conn;
        }

        public <T> T unwrap(Class<T> iface) throws SQLException {
            return delegate.unwrap(iface);
        }

        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            return delegate.isWrapperFor(iface);
        }

        public Statement createStatement() throws SQLException {
            return delegate.createStatement();
        }

        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return delegate.prepareStatement(sql);
        }

        public CallableStatement prepareCall(String sql) throws SQLException {
            return delegate.prepareCall(sql);
        }

        public String nativeSQL(String sql) throws SQLException {
            return delegate.nativeSQL(sql);
        }

        public void setAutoCommit(boolean autoCommit) throws SQLException {
            delegate.setAutoCommit(autoCommit);
        }

        public boolean getAutoCommit() throws SQLException {
            return delegate.getAutoCommit();
        }

        public void commit() throws SQLException {
            delegate.commit();
        }

        public void rollback() throws SQLException {
            delegate.rollback();
        }

        public void closeQuitely() {
            try {
                close();
            } catch (Throwable ex) {
                throw new RuntimeException(ex);
            }
        }

        public void close() throws SQLException {
            // do nothing
        }

        public boolean isClosed() throws SQLException {
            return delegate.isClosed();
        }

        public DatabaseMetaData getMetaData() throws SQLException {
            return delegate.getMetaData();
        }

        public void setReadOnly(boolean readOnly) throws SQLException {
            delegate.setReadOnly(readOnly);
        }

        public boolean isReadOnly() throws SQLException {
            return delegate.isReadOnly();
        }

        public void setCatalog(String catalog) throws SQLException {
            delegate.setCatalog(catalog);
        }

        public String getCatalog() throws SQLException {
            return delegate.getCatalog();
        }

        public void setTransactionIsolation(int level) throws SQLException {
            delegate.setTransactionIsolation(level);
        }

        public int getTransactionIsolation() throws SQLException {
            return delegate.getTransactionIsolation();
        }

        public SQLWarning getWarnings() throws SQLException {
            return delegate.getWarnings();
        }

        public void clearWarnings() throws SQLException {
            delegate.clearWarnings();
        }

        public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
            return delegate.createStatement(resultSetType, resultSetConcurrency);
        }

        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
                                                                                                          throws SQLException {
            return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
        }

        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
                                                                                                     throws SQLException {
            return delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
        }

        public Map<String, Class<?>> getTypeMap() throws SQLException {
            return delegate.getTypeMap();
        }

        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
            delegate.setTypeMap(map);

        }

        public void setHoldability(int holdability) throws SQLException {
            delegate.setHoldability(holdability);

        }

        public int getHoldability() throws SQLException {
            return delegate.getHoldability();
        }

        public Savepoint setSavepoint() throws SQLException {
            return delegate.setSavepoint();
        }

        public Savepoint setSavepoint(String name) throws SQLException {
            return delegate.setSavepoint(name);
        }

        public void rollback(Savepoint savepoint) throws SQLException {
            delegate.rollback(savepoint);

        }

        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
            delegate.releaseSavepoint(savepoint);
        }

        public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
                                                                                                               throws SQLException {
            return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
        }

        public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
                                                  int resultSetHoldability) throws SQLException {
            return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        }

        public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
                                             int resultSetHoldability) throws SQLException {
            return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
        }

        public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
            return delegate.prepareStatement(sql, autoGeneratedKeys);
        }

        public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
            return delegate.prepareStatement(sql, columnIndexes);
        }

        public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
            return delegate.prepareStatement(sql, columnNames);
        }

        public Clob createClob() throws SQLException {
            return delegate.createClob();
        }

        public Blob createBlob() throws SQLException {
            return delegate.createBlob();
        }

        public NClob createNClob() throws SQLException {
            return delegate.createNClob();
        }

        public SQLXML createSQLXML() throws SQLException {
            return delegate.createSQLXML();
        }

        public boolean isValid(int timeout) throws SQLException {
            return delegate.isValid(timeout);
        }

        public void setClientInfo(String name, String value) throws SQLClientInfoException {
            delegate.setClientInfo(name, value);
        }

        public void setClientInfo(Properties properties) throws SQLClientInfoException {
            delegate.setClientInfo(properties);
        }

        public String getClientInfo(String name) throws SQLException {
            return delegate.getClientInfo(name);
        }

        public Properties getClientInfo() throws SQLException {
            return delegate.getClientInfo();
        }

        public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
            return delegate.createArrayOf(typeName, elements);
        }

        public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
            return delegate.createStruct(typeName, attributes);
        }
    }
}
